import sys
import time

if sys.argv[0].split(".")[-1] == "exe":
    import loading_window
else:
    if sys.version < "3.6":
        print("启动失败！！！python版本必须大于等于3.6")
        time.sleep(3)
        exit()

import re
import os
import wx
import json
import queue
import base64
import threading
import requests
import wx.adv
import wx.media
import wx.richtext
import wx._xml
import shutil
import ctypes
import inspect
from Crypto.Cipher import AES
from urllib.parse import quote
from wx.lib.embeddedimage import PyEmbeddedImage



Smiles = PyEmbeddedImage(
    b"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAolJ"
    b"REFUOI1tk91Lk2EYxn/vx7OmwyBhVsytmY1cCBFBQWSRlYVGJ51EjajptliH4X/QQeJ5J+ZJ"
    b"f0AnIVSWJ0EQQfQhzNG3JkRQILrl3tft6sDNRvnAdfI8z8V939d13Vi2w79IpS4pHt8p17Vk"
    b"jK1EIqpcLqPN/to0nWwmrc5IUL29n3jw4Ajl8mVKpYvcv7+frq5HdEZqymauqZljWbYDwOlT"
    b"xxSL/eTOnfMY4wElYAWoAhXgI75fIJ/3mJ8/wvST5xaw3kE2k1YsBhMTtzEmCYTqxEaxVaCM"
    b"MRYTE1uIxZ6RzVxdf7Rsh2i0XZ5XkPRF0mONjo4KkJSSlBKgcDgsqVVSizwPRTuRZTu42Uxa"
    b"XV1rGNMCOECN8fHxpimX6OnpYW5uDmgF1TAEuXG9yuf5lOyZmYcMDUWAReAj8LqJXAEWWVhY"
    b"aLqrQaWVoZNtzMxMYRljq1S6iTFtwCrDw9+ZnPQ2hIOFuqB1crUKSx34qw6hXfMNG7264stM"
    b"TvrAEvAOKDaRBVpjd2cQ/BbwgwDY8XiEYvEb8AMokky+BF7UK+svGR+qNp+/L0O5jeKcRXxX"
    b"GLe//wxTU/fo7Q0AolCAUEiUGoWpAWtQtTi4rw19iUB5K1OPf9F//GTDRkue11K3ad0qBYMK"
    b"BAJyXUehoKv93e3S4l6p0Cfv1QlFdwS0EeXBwTT5/Op6mw389qn4Pr5jcTjZweunUVgOw1qA"
    b"/K0ig2fOsREky3YYGOjTyAjyPCTVUbWllW3St6T04ZC8N0c1cmGHBo4f0H/LNP3kueU4w3R3"
    b"w9gYzM6CX3HwKzaz71cYu/uV7rMvcLb2Mf3srbURi81WNJdLK5HYLuMi46LEng7lMlc2Xec/"
    b"xiMt8QU2mDwAAAAASUVORK5CYII=")


_print = print

DOWNLOADOS_PATH = os.path.join(os.path.dirname(sys.argv[0]), "download") 

if not os.path.exists(DOWNLOADOS_PATH):
    os.mkdir(DOWNLOADOS_PATH)


VERSION = "1.0.0"

M_file = ".ts"

M_giv = ".mp4"

Err_txt = "err.txt"

Key_txt = "key.txt"

Headers = {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,hmn;q=0.6",
        "Connection": "keep-alive",
        "sec-ch-ua": '" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"',
        "sec-ch-ua-mobile": "?0",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "cross-site",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
}

#代理
#import socks
#import socket
#socks.set_default_proxy(socks.SOCKS5, "127.0.0.1", 10808)
#socket.socket = socks.socksocket


def _async_raise(tid, exctype):
    try:
        tid = ctypes.c_long(tid)
        if not inspect.isclass(exctype):
            exctype = type(exctype)
        res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
        if res == 0:
            raise ValueError("invalid thread id")
        elif res != 1:
            ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
            raise SystemError("PyThreadState_SetAsyncExc failed")
    except Exception as err:
        print(err)


def requests_get(url, headers=None, **kw):
    try:
        return requests.get(url, headers=headers, **kw)
    except Exception as e:
        return Error(e, "text", status_code=800)


def os_mkdir(path):
    try:
        if not os.path.exists(path):
            os.mkdir(path)
        return True
    except FileNotFoundError:
        return False


class Error(object):
    def __init__(self, err, res, **kw):
        self.new_self(res, str(err.args))
        for key, value in kw.items():
            self.new_self(key, value)

    def new_self(self, obj, value):
        if not hasattr(self, obj):
            setattr(self, obj, value)



class FileDrop(wx.FileDropTarget):
    def __init__(self,windows):
        wx.FileDropTarget.__init__(self)
        self.windows = windows

    def OnDropFiles(self, x, y, filePath):
        if filePath:
            for i in filePath:
                self.windows.SetValue(i)
            return True
        else:
            return False



class maingui(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__ ( self, parent, size = wx.Size( 900,500 ))
        self.SetTitle("乐鱼采集 {}".format(VERSION))
        self.main_url = "https://www.ylzy1.com/index.php/vod/search.html?wd={}&search_button="
        self.next_url = "https://www.ylzy1.com/index.php/index/index/page/{}/wd/{}.html"
        self.url_url = "https://www.ylzy1.com/index.php/vod/detail/id/{}html"
        boxv = wx.BoxSizer( wx.VERTICAL )
        boxh = wx.BoxSizer( wx.HORIZONTAL )
        boxm = wx.BoxSizer( wx.HORIZONTAL )
        self.search = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
        self.buttn1 = wx.Button(self, label="搜索")
        self.play_botton = wx.Button(self, label="播放库")
        
        if sys.argv[0].split(".")[-1] != "exe":
            self.buttn_build = wx.Button(self, label="构建exe程序")
            self.update_button = wx.Button(self, label="检查更新")
            boxh.Add(self.buttn_build, 0, wx.ALIGN_CENTER_VERTICAL, 5 )
            boxm.Add(self.update_button, 1, wx.EXPAND, 5)
            self.update_button.Bind(wx.EVT_BUTTON, self.on_update)
            self.buttn_build.Bind(wx.EVT_BUTTON, self.on_build)

        boxm.Add(self.play_botton, 1, wx.EXPAND, 5)
        boxh.Add(self.search, 1, wx.ALIGN_CENTER_VERTICAL, 5 )
        boxh.Add(self.buttn1, 0, wx.ALIGN_CENTER_VERTICAL, 5 )
        boxv.Add(boxm, 1, wx.EXPAND, 5 )
        boxv.Add(boxh, 1, wx.EXPAND, 5 )
        self.buttn2 = wx.Button(self)
        self.buttn2.SetLabel("自定义下载")
        boxv.Add(self.buttn2, 0, wx.EXPAND, 5 )
        box_v = wx.BoxSizer( wx.VERTICAL )
        self.scroller = wx.ScrolledWindow(self)
        self.scroller.SetScrollRate(0,20)
        box_v.Add(self.scroller, 1, wx.EXPAND, 5 )
        boxv.Add(box_v, 2, wx.EXPAND, 5 )
        self.boxV = wx.BoxSizer( wx.VERTICAL )
        self.scroller.SetSizer(self.boxV)
        self.buttn2.Bind(wx.EVT_BUTTON, lambda evt, mark=1 : self.m_button(evt, mark))
        self.buttn1.Bind(wx.EVT_BUTTON, lambda evt, mark=2 : self.m_button(evt, mark))
        self.play_botton.Bind(wx.EVT_BUTTON, self.on_openmp4ui)
        self.search.Bind(wx.EVT_TEXT_ENTER, lambda evt, mark=2 : self.m_button(evt, mark))
        self.SetSizer(boxv)
        self.Layout()
        self.Centre( wx.BOTH )
        self.Bind(wx.EVT_CLOSE, self.m_close)

    def m_close(self, eve):
        wx.Exit()
        sys.exit()

    def addwx(self, url_txt, name, model):
        _print("{} {} {}".format(url_txt, name, model))
        boxh = wx.BoxSizer( wx.HORIZONTAL )
        http = wx.adv.HyperlinkCtrl(self.scroller, label=url_txt, url=url_txt)
        buttn1 = wx.Button(self.scroller, label=name)
        buttn2 = wx.Button(self.scroller, label=model)
        buttn1.Enable(False)
        buttn2.Enable(False)
        buttn3 = wx.Button(self.scroller, label="下载")
        buttn3.Bind(wx.EVT_BUTTON, lambda evt, mark=(name.replace(" ",""), url_txt) : self.m_button(evt, mark))
        boxh.Add(buttn1, 1, wx.ALIGN_CENTER_VERTICAL, 5 )
        boxh.Add(http, 1, wx.ALIGN_CENTER_VERTICAL, 5 )
        boxh.Add(buttn2, 0, wx.ALIGN_CENTER_VERTICAL, 5 )
        boxh.Add(buttn3, 0, wx.ALIGN_CENTER_VERTICAL, 5 )
        self.boxV.Add(boxh, 1, wx.EXPAND, 5 )
        self.Layout()

    def on_openmp4ui(self, eve):
        gui_myplay(self, ).Show(True)

    def on_update(self, eve):
        self.update_button.Enable(False)
        threading.Thread(target=my_update, args=(self, lambda : self.update_button.Enable(True))).start()

    def on_build(self, eve):
        self.buttn_build.Enable(False)
        threading.Thread(target=my_build, args=(self,)).start()

    def m_button(self, eve, mak):
        if mak == 1: 
            mygui(self).Show()
        elif mak == 2:
            name = re.sub("[\n\t\r]","", self.search.GetValue())
            if name == "":
                wx.MessageBox("缺少搜索信息", "提示",wx.OK, self)
            else:
                self.scroller.DestroyChildren()
                self.buttn1.Enable(False)
                def mydef(windows):
                    def send(for_data):
                        for addr in for_data:
                            wx.CallAfter(windows.addwx, self.url_url.format(addr[0]), addr[1], addr[2] + " " + addr[3])
                    r = requests_get(self.main_url.format(quote(name)), headers=Headers)
                    if r.status_code == 200:
                        sui = re.findall('<a href="/index.php/index/index/page/\d+/wd/.*?html">(\d+)</a>', r.text)
                        rexml = '<a class="videoName" href="/index.php/vod/detail/id/(\d+).html".*?">(.*?)<img .*?<span class="region".*?>(.*?)<.*?<span class="category">(.*?)<.*?</li>'
                        send(re.findall(rexml, r.text,re.S))
                        if sui:
                            for i in range(1,int(sui[-1])+1):
                                r = requests_get(self.next_url.format(i, quote(name)), headers=Headers)
                                if r.status_code == 200:
                                    send(re.findall(rexml, r.text,re.S))
                    windows.buttn1.Enable(True)
                threading.Thread(target=mydef,args=(self,)).start()
        else:
            mygui(self, mak[0], mak[1]).Show()
        eve.Skip()



class mygui(wx.Frame):
    def __init__(self, parent, name="", url=""):
        wx.Frame.__init__ ( self, parent, size = wx.Size( 800, 500 ))
        self.SetTitle("乐鱼下载 {}".format(VERSION))
        end_left_sizer = wx.BoxSizer( wx.VERTICAL )
        sizer = wx.BoxSizer( wx.HORIZONTAL )
        nametxt = wx.StaticText(self, label="电影名字:")
        self.name = wx.TextCtrl(self, value=name, style=wx.TE_PROCESS_ENTER)
        sizer.Add(nametxt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        sizer.Add(self.name, 4, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        end_left_sizer.Add(sizer, 1, wx.EXPAND, 5 )
        sizer = wx.BoxSizer( wx.HORIZONTAL )
        nametxt = wx.StaticText(self, label="地址链接:")
        self.url = wx.TextCtrl(self, value=url, style=wx.TE_PROCESS_ENTER)
        sizer.Add(nametxt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        sizer.Add(self.url, 4, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        end_left_sizer.Add(sizer, 1, wx.EXPAND, 5 )
        sizer = wx.BoxSizer( wx.HORIZONTAL )
        nametxt = wx.StaticText(self, label="线程数量:")
        self.spin = wx.SpinCtrl(self, min=1, max=18)
        self.spin.SetValue(0)
        self.repair = wx.Button(self, label="修复")
        sizer.Add(nametxt, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        sizer.Add(self.spin, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        sizer.Add(self.repair, 1, wx.ALIGN_CENTER_VERTICAL, 2 )
        end_left_sizer.Add(sizer, 1, wx.EXPAND, 5 )

        end_right_sizer = wx.BoxSizer( wx.VERTICAL )
        self.mcheck = wx.Choice(self, choices=["全部下载","分集下载"])
        self.mcheck.SetSelection(0)
        end_right_sizer.Add(self.mcheck, 1, wx.ALL|wx.EXPAND, 5)
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.start_en = wx.SpinCtrl(self, min=1, max=999)
        nametxt = wx.StaticText(self, label="开始:")
        sizer.Add(nametxt, 0, wx.ALL, 5 )
        sizer.Add(self.start_en, 0, wx.ALL, 5)
        end_right_sizer.Add(sizer, 1, wx.EXPAND, 5)
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        nametxt = wx.StaticText(self, label="结束:")
        self.stop_en  = wx.SpinCtrl(self, min=1, max=999)
        sizer.Add(nametxt, 0, wx.ALL, 5 )
        sizer.Add(self.stop_en, 0, wx.ALL, 5)
        end_right_sizer.Add(sizer, 1, wx.EXPAND, 5)

        self.button = wx.Button(self, label="开始")
        hbsizer = wx.BoxSizer(wx.VERTICAL)
        vbsizer = wx.BoxSizer(wx.HORIZONTAL)
        vbsizer.Add(end_left_sizer, 1, wx.EXPAND, 5)
        vbsizer.Add(end_right_sizer, 0, wx.EXPAND, 5)
        hbsizer.Add(vbsizer, 2, wx.EXPAND, 5)
        hbsizer.Add(self.button, 1, wx.EXPAND, 5)
        
        self.logsizer = wx.BoxSizer( wx.HORIZONTAL )
        bsizer = wx.BoxSizer( wx.VERTICAL )
        bsizer.Add(self.logsizer, 2, wx.EXPAND, 5 )
        bsizer.Add(hbsizer, 0, wx.EXPAND, 5 )
        self.SetSizer( bsizer )
        self.Layout()
        self.Centre( wx.BOTH )
        self.mpath = None 
        self.mrun_thread = None
        self.dic = {}
        self.select(0)
        self.m_choice(0)
        self.url.SetDropTarget(FileDrop(self.url))
        self.spin.Bind(wx.EVT_SPINCTRL, self.select)
        self.mcheck.Bind(wx.EVT_CHOICE, self.m_choice)
        self.repair.Bind(wx.EVT_BUTTON, lambda evt, mark=0 : self.mystart(evt, mark))
        self.button.Bind(wx.EVT_BUTTON, lambda evt, mark=1 : self.mystart(evt, mark))
        self.Bind(wx.EVT_CLOSE, self.m_close)

    def m_close(self, eve):
        if self.mrun_thread and self.mrun_thread.is_run:       
            aws = wx.MessageBox("确定关闭吗？此窗口正在运行的任务将会被清除和停止", "提示", wx.YES_NO, self)
            if aws == wx.YES:
                self.mrun_thread._stop()
                if self.mpath and os.path.exists(self.mpath):
                    for files in os.listdir(self.mpath):
                        if os.path.isdir(os.path.join(self.mpath, files)):
                            shutil.rmtree(os.path.join(self.mpath, files))
                self.Destroy()
        else:
            self.Destroy()

    def m_choice(self, eve):
        if self.mcheck.GetSelection() == 0:
            self.start_en.Enable(False)
            self.stop_en.Enable(False)
        elif self.mcheck.GetSelection() == 1:
            self.start_en.Enable(True)
            self.stop_en.Enable(True)

    def select(self, eve):
        spinV = self.spin.GetValue()
        dicV  = len(self.dic)
        if dicV < spinV:
            for i in range(dicV, spinV):
                mylog = guiprint(self)
                self.logsizer.Add(mylog, 1, wx.EXPAND,5)
                self.dic.update({str(i): mylog})
        elif spinV < dicV:
            for i in range(dicV, spinV, -1):
                self.logsizer.Detach(self.dic[str(i-1)])
                self.dic[str(i-1)].Close()
                self.dic[str(i-1)].Destroy()
                self.dic.pop(str(i-1))
        self.Layout()

    def mystart(self,eve,mak):
        _print("{}".format(self.dic))
        name = re.sub('[\n\t\r/:<>?|*"]', "", self.name.GetValue())
        url  = re.sub("[\n\t\r]", "", self.url.GetValue())
        if not name or not url:
            wx.MessageBox("缺少一个参数", "提示",wx.OK, self)
            return
        self.mpath = os.path.join(DOWNLOADOS_PATH, name)
        if not os_mkdir(self.mpath):
            wx.MessageBox("电影命名名称有误请重新命名，命名不能包含特殊符号", "提示", wx.OK, self)
            return 
        if mak ==  1:
            self.enables(False)
            if url.split(".")[-1] == "m3u8":
                if url[0:4] == "http":
                    self.mrun_thread = mythread(Call_class.M3U8_url_call, self, url, name, self.dic[list(self.dic)[0]], self.mpath)
                elif os.path.exists(url):
                    self.mrun_thread = mythread(Call_class.M3U8_file_call, self, url, name, self.dic[list(self.dic)[0]], self.mpath)
            else:
                self.mrun_thread = mythreads(self, name, url, self.mpath)
            self.mrun_thread.start()    
        else:
            self.repair.Enable(False)
            if not os.path.exists(os.path.join(self.mpath, Err_txt)):
                if self.button.IsEnabled():
                    def my_file_repair():
                        if os.path.exists(self.mpath):
                            for files in os.listdir(self.mpath):
                                if os.path.isdir(os.path.join(self.mpath, files)):
                                    shutil.rmtree(os.path.join(self.mpath, files))
                        #self.Destroy()
                        wx.CallAfter(self.m_close, None)
                    threading.Thread(target=my_file_repair).start()
                else:
                    self.repair.Enable(True)
            else:
                self.add_err_log()
                self.mrun_thread = mythread(err_modir, self, self.dic["err"], self.mpath, self.button.IsEnabled())
                self.mrun_thread.start()
            self.enables(False)
        eve.Skip()

    def enables(self, bools):
        self.button.Enable(bools)
        self.spin.Enable(bools)
        self.name.Enable(bools)
        self.url.Enable(bools)
        self.mcheck.Enable(bools)
        self.start_en.Enable(bools)
        self.stop_en.Enable(bools)

    def add_err_log(self):
        if "err" not in self.dic:
            mylog = guiprint(self)
            self.logsizer.Add(mylog, 1, wx.EXPAND,5)
            self.dic.update({"err": mylog})
            self.Layout()

    def del_err_log(self):
        if "err" in self.dic:
            self.logsizer.Detach(self.dic["err"])
            self.dic["err"].Close()
            self.dic["err"].Destroy()
            self.dic.pop("err")
            self.Layout()

    def mydialog(self, mesg, tle):
        dlg = MyDialog(self, mesg, tle)
        dlg.ShowModal()
        dlg.Destroy() 



class guiprint(wx.richtext.RichTextCtrl):
    def __init__(self, parent):
        wx.richtext.RichTextCtrl.__init__(self, parent, style=wx.richtext.RE_READONLY)
        self.SetValue(time.strftime("%Y-%m-%d %H:%M:%S"))

    def m_clear(self):
        self.Clear()

    def appendtxt(self, txt):
        self.AddParagraph(txt)
        self.AppendText(" ")
        self.ShowPosition(-1)



class gui_myplay(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__ ( self, parent, size = wx.Size( 800, 500 ))
        self.windows = parent
        self.windows.play_botton.Enable(False)
        self.Bind(wx.EVT_CLOSE, self.m_close)
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.mymedia = gui_media(self, None)
        sizer.Add(self.mymedia, 3, wx.EXPAND, 10)
        self.mytree = mTreeCtrl(self)
        sizer.Add(self.mytree, 1, wx.EXPAND, 10)
        self.SetSizer( sizer )
        self.Layout()
        self.Centre( wx.BOTH )

    def m_close(self, eve):
        self.windows.play_botton.Enable(True)
        self.Destroy()



class gui_media(wx.Panel):
    def __init__(self, parent, file_name):
        wx.Panel.__init__(self, parent)
        self.progress = False
        sizer = wx.BoxSizer( wx.VERTICAL )
        self.media_ctrl = wx.media.MediaCtrl(self, style=wx.SIMPLE_BORDER, szBackend="")
        sizer.Add( self.media_ctrl, 1, wx.EXPAND, 5 )
        m_Hsizer = wx.BoxSizer( wx.HORIZONTAL )
        m_Vsizer = wx.BoxSizer( wx.VERTICAL )
        buttonv = wx.BoxSizer(wx.HORIZONTAL)
        self.my_panel = wx.Panel(self)
        self.status_button = wx.Button(self.my_panel, label="播放")
        self.all_button = wx.Button(self.my_panel, label="全屏(p)")
        buttonv.Add(self.status_button, 1, wx.EXPAND, 5 )
        buttonv.Add(self.all_button, 0, wx.EXPAND, 5 )
        self.m_slider1 = wx.Slider( self.my_panel, value=0, minValue=0, maxValue=100)
        self.progresstext = wx.StaticText(self.my_panel, style=wx.ALIGN_CENTRE_HORIZONTAL|wx.ST_NO_AUTORESIZE)
        m_Vsizer.Add(self.progresstext, 1, wx.ALL | wx.EXPAND, 5 )
        m_Vsizer.Add(self.m_slider1, 1, wx.EXPAND, 5 )
        m_Vsizer.Add( buttonv, 1, wx.ALL | wx.EXPAND, 5 )
        m_Vsizer.Add( m_Hsizer, 0, wx.EXPAND, 5 )
        self.my_panel.SetSizer(m_Vsizer)
        sizer.Add( self.my_panel, 0, wx.EXPAND, 5 )
        self.Bind(wx.media.EVT_MEDIA_STOP, self.on_media_status)
        self.m_slider1.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_progress)
        self.m_slider1.Bind(wx.EVT_LEFT_UP, self.on_mouse_progress)
        self.m_slider1.Bind(wx.EVT_SLIDER, self.on_mouse_progress)
        self.all_button.Bind(wx.EVT_BUTTON, self.on_button)
        self.status_button.Bind(wx.EVT_BUTTON, self.on_button)
        self.m_slider1.Bind(wx.EVT_SCROLL_CHANGED, self.on_set_progress_bar)
        self.status_button.Enable(False)
        self.SetSizer(sizer)
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.on_Timer)
        ensic = [wx.AcceleratorEntry(wx.ACCEL_NORMAL, 32, self.status_button.GetId()), wx.AcceleratorEntry(wx.ACCEL_NORMAL, ord("p"), self.all_button.GetId())]
        self.SetAcceleratorTable(wx.AcceleratorTable(ensic))
        self.Bind(wx.EVT_LEFT_DCLICK, self.on_button)
        self.Bind(wx.EVT_CLOSE, self.on_close)
        wx.CallAfter(lambda file_name=file_name : self.on_create(file_name))

    def on_close(self, eve):
        self.progress = False
        self.media_ctrl.Stop()
        if self.timer.IsRunning():
            self.timer.Stop()
        self.Destroy()

    def on_create(self, file_name):
        if file_name:
            self.progress = True
            self.GetParent().SetTitle(file_name)
            self.media_ctrl.Load(file_name)
            self.media_ctrl.SetInitialSize()
        self.GetSizer().Layout()

    def on_Timer(self, eve):
        if self.progress and self.media_ctrl:
            offset = self.media_ctrl.Tell()
            m = offset//1000
            self.progresstext.SetLabel("{}:{}:{}".format(m//60//60, (m//60)-(m//60//60)*60, m-(m//60)*60))
            self.m_slider1.SetValue(offset)

    def on_media_status(self, eve):
        self.status_button.Enable(True)
        if self.status_button.GetLabel() == "播放":
            self.m_slider1.SetRange(0, self.media_ctrl.Length())
        self.status_button.SetLabel("播放")
        if not self.timer.IsRunning():
            self.timer.Start(1000)
        else:
            self.timer.Stop()

    def on_mouse_progress(self, eve):
        if eve.EventType == wx.EVT_LEFT_DOWN.typeId:
            self.progress = False
        elif eve.EventType == wx.EVT_LEFT_UP.typeId:
            self.progress = True
        else:
            m = self.m_slider1.GetValue()//1000
            self.progresstext.SetLabel("{}:{}:{}".format(m//60//60, (m//60)-(m//60//60)*60, m-(m//60)*60))
        eve.Skip()

    def on_set_progress_bar(self, eve):
        offset = self.m_slider1.GetValue() - self.media_ctrl.Tell()
        self.media_ctrl.Seek(offset, wx.FromCurrent)

    def on_button(self, eve):
        if eve.GetEventObject().GetLabel() == "播放":
            eve.GetEventObject().SetLabel("暂停")
            self.media_ctrl.Play()
        elif eve.GetEventObject().GetLabel() == "暂停":
            self.media_ctrl.Pause()
            eve.GetEventObject().SetLabel("播放")
        elif eve.GetEventObject().GetLabel() == "全屏(p)":
            if self.GetParent().mytree.IsShown():
                self.GetParent().Maximize(True)
                self.my_panel.Show(False)
                self.GetParent().mytree.Show(False)
            else:
                self.GetParent().Maximize(False)
                self.my_panel.Show(True)
                self.GetParent().mytree.Show(True)
            self.GetParent().Layout()



class mTreeCtrl(wx.TreeCtrl):
    def __init__(self, parent, *a, **kw):
        wx.TreeCtrl.__init__(self, parent, *a, **kw)
        isz = (16,16)
        il = wx.ImageList(isz[0], isz[1])
        self.fldridx     = il.Add(wx.ArtProvider.GetBitmap(wx.ART_FOLDER,      wx.ART_OTHER, isz))
        self.fldropenidx = il.Add(wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN, wx.ART_OTHER, isz))
        self.fileidx     = il.Add(wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, isz))
        self.smileidx    = il.Add(Smiles.GetBitmap())
        self.SetImageList(il)
        self.il = il
        root_path = DOWNLOADOS_PATH
        self.root = self.AddRoot("播放列表(download)")
        self.SetItemData(self.root, None)
        self.SetItemImage(self.root, self.fldridx, wx.TreeItemIcon_Normal)
        self.SetItemImage(self.root, self.fldropenidx, wx.TreeItemIcon_Expanded)
        self.Expand(self.root)
        rmenu = wx.Menu()
        self._push = rmenu.Append(-1, "刷新")
        self._remove = rmenu.Append(-1, "删除")
        self.Bind(wx.EVT_MENU, lambda eve, path=root_path: self.on_menu(eve, path), self._push)
        self.Bind(wx.EVT_MENU, lambda eve, path=root_path: self.on_menu(eve, path), self._remove)
        self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, lambda eve, menu=rmenu: self.on_ShowPopup(eve, menu))
        self.Bind(wx.EVT_LEFT_DCLICK, self.on_click)
        wx.CallAfter(lambda : self.gettree(root_path, self.root))

    def on_ShowPopup(self, eve, menu):
        if not self.GetItemData(eve.GetItem()):
            menu.Enable(self._remove.GetId(), False)
        else:
            self.SelectItem(eve.GetItem())
            menu.Enable(self._remove.GetId(), True)
        self.PopupMenu(menu)

    def on_menu(self, eve, root_path):
        if eve.Id == self._remove.GetId():
            f = self.GetSelection()
            if f.IsOk():
                r = self.GetItemData(f)
                if r and os.path.exists(r):
                    if os.path.isfile(r):
                        os.remove(r)
                    else:
                        shutil.rmtree(r)
                    self.Delete(f)
        elif eve.Id == self._push.GetId():
            self.DeleteAllItems()
            self.root = self.AddRoot("播放列表")
            self.SetItemData(self.root, None)
            self.SetItemImage(self.root, self.fldridx, wx.TreeItemIcon_Normal)
            self.SetItemImage(self.root, self.fldropenidx, wx.TreeItemIcon_Expanded)
            self.Expand(self.root)
            self.gettree(root_path, self.root)
        eve.Skip()

    def on_click(self, eve):
        f = self.HitTest(eve.GetPosition())
        if f[0].IsOk():
            r = self.GetItemData(f[0])
            if r and not os.path.isdir(r) and r.split(".")[-1] == "mp4":
                self.GetParent().mymedia.on_create(r)
        eve.Skip()

    def gettree(self, path, dirp):
        trees = os.listdir(path)
        for v in trees:
            path_1 = os.path.join(path, v)
            if os.path.isdir(path_1):
                child = self.AppendItem(dirp, v)
                self.SetItemData(child, path_1)
                self.SetItemImage(child, self.fldridx, wx.TreeItemIcon_Normal)
                self.SetItemImage(child, self.fldropenidx, wx.TreeItemIcon_Expanded)
                self.gettree(path_1, child)
            elif path_1.split(".")[-1] == "mp4":
                item = self.AppendItem(dirp,  v)
                self.SetItemData(item, path_1)
                self.SetItemImage(item, self.fileidx, wx.TreeItemIcon_Normal)
                self.SetItemImage(item, self.smileidx, wx.TreeItemIcon_Selected)
        self.Layout()



class MyDialog(wx.Dialog):
    def __init__(self, parent, message, title, ttl=3.5):
        wx.Dialog.__init__(self, parent, title=title)
        self.timeToLive = ttl*1000
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer1 = wx.BoxSizer(wx.VERTICAL)
        sizer1.Add(wx.StaticText(self, label=message, style=wx.ST_ELLIPSIZE_START), wx.SizerFlags().Border().Center())
        sizer.Add(sizer1, wx.SizerFlags(1).Border().Center())
        self._next = 1
        self.SetSizer(sizer)
        self.Layout()
        self.Centre( wx.BOTH )
        self.SetSizer(sizer)
        self.timer = wx.Timer(self)
        self.timer.Start(100)
        self.Bind(wx.EVT_TIMER, self.on_Timer, self.timer)
        self._positon = self.GetPosition()

    def on_Timer(self, evt):
        self.Move(self._positon[0], self._positon[1]-self._next)
        self._next +=1
        self.timeToLive -= 100
        if self.timeToLive == 0:
            self.timer.Stop()
            self.Destroy()



class mythread(threading.Thread):
    def __init__(self, call, *argv):
        threading.Thread.__init__(self)
        self.daemon = True
        self._call = call
        self._argv = argv
        self.is_run = False

    def run(self):
        self.is_run = True
        self._call(*self._argv)
        self.is_run = False

    def _stop(self):
        if self.is_run:
            _async_raise(self.ident, SystemExit)
            self.is_run = False



class mythreads(threading.Thread):
    def __init__(self, windows, name, url, mpath):
        threading.Thread.__init__(self)
        self.windows = windows
        self.name    = name
        self.url     = url
        self.path    = mpath
        self.queue_list = [queue.Queue() for v in range(self.windows.spin.GetValue())]
        self.runthr  = []
        self.is_run = False
        self.daemon = True

    def run(self):
        self.is_run = True
        r = requests_get(self.url, headers=Headers)
        if r.status_code == 200:
            addr     = re.findall("http.*?m3u8", r.text)
            url_addr = list(set(addr))
            url_addr.sort(key=addr.index)
            self._queue = queue.Queue()
            for _id, que in enumerate(self.queue_list):
                thr = threading.Thread(target=Call_class.queue_call, args=(que, self._queue, _id, self.windows.dic[str(_id)], self.path))
                thr.daemon = True
                thr.start()
                self.runthr.append(thr)

            def yieldset(value):
                for k, v in enumerate(value):
                    yield k, v
            select = 0
            _set = set()
            callnext = yieldset(url_addr)   
            while 1:
                Id = self._queue.get()
                if select == 0:
                    sendmsg = ""
                    try:
                        while True:
                            retd, retv = next(callnext)
                            if not os.path.exists(os.path.join(self.path, str(retd+1) + M_giv)):
                                if self.windows.mcheck.GetSelection() == 0:
                                    _print("分发对象：{} 分发数据：{} {}".format(self.queue_list[Id], retv, str(retd + 1)))
                                    self.queue_list[Id].put({"call":True, "url":retv, "id": str(retd + 1)})
                                    break
                                elif self.windows.stop_en.GetValue() >= retd + 1 >= self.windows.start_en.GetValue():
                                    _print("分发对象：{} 分发数据：{} {}".format(self.queue_list[Id], retv, str(retd + 1)))
                                    self.queue_list[Id].put({"call":True, "url":retv, "id": str(retd + 1)})
                                    break
                                else:
                                    print("略过", retd, retv)
                            else:
                                sendmsg += "[" + str(retd+1) + "]"
                    except StopIteration:
                        select = 1
                    finally:
                        if sendmsg:
                            _print("{}第{}集已下载，自动略过".format(os.path.basename(self.path), sendmsg))
                            wx.CallAfter(self.windows.mydialog, "{}第{}集已下载，自动略过".format(os.path.basename(self.path), sendmsg), "提示")
                elif select == 1:
                    self.queue_list[Id].put({"call":False})
                    _set.add(str(Id))
                    if len(_set) >= len(self.queue_list):
                        break
        wx.CallAfter(self.windows.enables, True )
        self.is_run = False

    def _stop(self):
        for thr in self.runthr:
            if thr.is_alive():
                _async_raise(thr.ident, SystemExit)
        if self.is_run:        
            _async_raise(self.ident, SystemExit)
            self.is_run = False


class Call_class(object):
    @classmethod
    def queue_call(self, cls_queue, parent_queue, call_id, obj_gui, path):
        def queue_get():
            while 1:
                if cls_queue.empty():
                    parent_queue.put(call_id)
                try:
                    ret = cls_queue.get(timeout=5)
                except queue.Empty:
                    _print("queue超时 {}".format(call_id))
                    continue
                return ret
        while 1:        
            data = queue_get()
            if not data["call"]:
                _print("完成退出 {}".format(call_id))
                return      
            _print("初始化 {} 第{}集".format(data["url"],data["id"]))    
            call_main(data["url"], data["id"], obj_gui, path)

    @classmethod
    def M3U8_file_call(self, windows, file_path, name, obj_gui, path):
        m_path = os.path.join(path, name)
        os_mkdir(m_path)
        with open(file_path,"r") as f:
            list_data = f.readlines()
        download(m_path, "", list_data, name, obj_gui, path)
        wx.CallAfter(windows.enables, True )

    @classmethod
    def M3U8_url_call(self, windows, url, name, obj_gui, path):
        call_main(url, name, obj_gui, path)
        wx.CallAfter(windows.enables, True )


def list_addr(com, addr, mpath):
    url_adds = []
    key = ""
    for index,line in enumerate(addr):
        if "#EXT-X-KEY" in line:
            method_pos = line.find("METHOD")
            comma_pos = line.find(",")
            method = line[method_pos:comma_pos].split('=')[1]
            uri_pos = line.find("URI")
            quotation_mark_pos = line.rfind('"')
            key_url = line[uri_pos:quotation_mark_pos].split('"')[1]
            res = requests_get(key_url)
            key = res.content
            with open(os.path.join(mpath, Key_txt),"w") as f:
                f.write(base64.b64encode(res.content).decode("utf-8"))
        elif 'http' in line:
            url_adds.append(line)
        elif "/" in line:
            url_adds.append(com + line)
    if(len(key)):
        cryptor = AES.new(key, AES.MODE_CBC, key)
    else:
        cryptor = None
    return cryptor, url_adds


def m_requests(cryptor, file_path, url, bar, mtime, gui):
    url = re.sub("[\n\t\r]", "", url)
    res = requests_get(url, headers=Headers, timeout=(30, 90))
    if res.status_code == 200:
        wx.CallAfter(gui.appendtxt,"...{} [{} % - {}完成] {}".format(file_path[-15:], bar, mtime, time.strftime("%H:%M:%S")))
        try:
            with open(file_path,'wb') as file:
                if cryptor:
                    file.write(cryptor.decrypt(res.content))
                else:
                    file.write(res.content)
        except Exception:
            write_err("1 {} {} 写入错误{}".format(url, file_path, res.status_code), os.path.dirname(os.path.dirname(file_path)))
    else:
        write_err("0 {} {} 响应码错误{}".format(url, file_path, res.status_code), os.path.dirname(os.path.dirname(file_path)))


def write_err(err, path):
    with open(os.path.join(path, Err_txt), "a", encoding='utf-8') as er:
        er.write(err + "\n")


def write_data(mid, sumd, mpath, gui):
    m_path = os.path.dirname(mpath)
    wx.CallAfter(gui.appendtxt,"合成中... {} ".format(os.path.join(m_path, mid + M_giv)))
    h = open(os.path.join(m_path, mid + M_giv), "wb")
    for i in range(0,sumd):
        try:
            with open(os.path.join(mpath, str(i) + M_file), "rb") as r:
                h.write(r.read())
        except Exception as e:
            wx.CallAfter(gui.appendtxt,"{} {}".format(i, str(e)))
    h.close()


def err_modir(windows, gui, mpath, mbool):
    wx.CallAfter(gui.appendtxt,"开始: {}".format(time.strftime("%Y-%m-%d %H-%M-%S")))
    dic_addr = {}
    with open(os.path.join(mpath, Err_txt), "r", encoding='utf-8') as f:
        dataline = f.readlines()
    os.remove(os.path.join(mpath, Err_txt))
    if os.path.exists(os.path.join(mpath, Key_txt)):
        with open(os.path.join(mpath, Key_txt),"r") as f:
            key = base64.b64decode(f.read().encode("utf-8"))
            cryptor = AES.new(key, AES.MODE_CBC, key)
    else:
        cryptor = None
    sum_dr = len(dataline)   
    start  = time.time()
    for k,v in enumerate(dataline):
        url  = v.split(" ")[1]
        path = v.split(" ")[2]
        try:
            bar = round(100*(k/sum_dr), 3)
            mtime = (100-bar)/(bar/(time.time() - start))
            mtime = "预计" + str(int(mtime//60)) + " 分 " + str(int(mtime - (mtime//60)*60)) + " 秒"
        except ZeroDivisionError:
            mtime = "0 分 0 秒"
        mid  = os.path.basename(os.path.dirname(path))
        dic_addr.update({mid:{"name":path.split("\\")[-2], "path":os.path.dirname(path)}})
        m_requests(cryptor, path, url, bar, mtime, gui)
    for addr in dic_addr:
        sumd = os.listdir(dic_addr[addr]["path"])
        write_data(dic_addr[addr]["name"], len(sumd), dic_addr[addr]["path"], gui)
    wx.CallAfter(gui.appendtxt,"完成: {}".format(time.strftime("%Y-%m-%d %H-%M-%S")))
    windows.repair.Enable(True)
    wx.CallAfter(windows.del_err_log)
    if mbool:
        wx.CallAfter(windows.enables, True )


def call_main(url, mid, gui, mpath):
    m_path = os.path.join(mpath, mid)
    os_mkdir(m_path)
    wx.CallAfter(gui.m_clear)
    wx.CallAfter(gui.appendtxt,"开始: {}".format(time.strftime("%Y-%m-%d %H-%M-%S")))
    com = url.split("/")[0] + "//" + url.split("/")[2]
    wx.CallAfter(gui.appendtxt,"get {}".format(url))
    res = requests_get(url, headers=Headers)
    if res.status_code == 200:
        url_m3u8 = com + res.text.split("\n")[2]
        wx.CallAfter(gui.appendtxt,"get {}".format(url_m3u8))
        res = requests_get(url_m3u8, headers=Headers)
        if res.status_code == 200:
            download(m_path,com, res.text.split("\n"), mid, gui, mpath)


def download(m_path, com, url_list, mid, gui, mpath):
    aes, url_addr = list_addr(com, url_list, mpath)
    wx.CallAfter(gui.appendtxt,"{} {}".format(len(url_addr),url_addr[0]))
    sum_dr = len(url_addr)
    start = time.time()
    listfile = os.listdir(m_path)
    if listfile:
        os.remove(os.path.join(m_path, listfile[-1]))
    for k, v in enumerate(url_addr):
        if os.path.exists(os.path.join(m_path, str(k) + M_file)):
            continue
        try:
            bar = round(100*(k/sum_dr), 3)
            mtime = (100-bar)/(bar/(time.time() - start))
            mtime = "预计" + str(int(mtime//60)) + " 分 " + str(int(mtime - (mtime//60)*60)) + " 秒"
        except ZeroDivisionError:
            mtime = "0 分 0 秒"
        m_requests(aes, os.path.join(m_path, str(k) + M_file), v, bar, mtime, gui)
    list_file = os.listdir(m_path)
    write_data(mid, len(list_file), m_path, gui)
    wx.CallAfter(gui.appendtxt,"完成:{}".format(time.strftime("%Y-%m-%d %H-%M-%S")))



#更新 

def my_update(windows, Mcall):
    url = "https://gitee.com/xuewumeng/Project/raw/master/%E4%B9%90%E9%B1%BC%E9%87%87%E9%9B%86/version.txt"
    res = requests.get(url)
    if res.status_code == 200:
        if res.text.split("\n")[0] != VERSION:
            res = requests.get(res.text.split("\n")[1])
            if res.status_code == 200:
                with open(__file__, "w", encoding='utf-8') as f:
                    f.write(res.text)
                wx.MessageBox("更新成功", "提示", wx.OK, windows)
                wx.Exit()
                os.execl(sys.executable, sys.executable, sys.argv[0])
        else:
            wx.MessageBox("当前是最新代码了", "提示", wx.OK, windows)
    else:
        wx.MessageBox("响应错误，请一会再重试", "提示", wx.OK, windows)
    Mcall()



#自动构建exe

def my_build(windows):
    windows.Hide()
    url = "https://gitee.com/xuewumeng/Project/raw/master/%E4%B9%90%E9%B1%BC%E9%87%87%E9%9B%86/images.jpg"
    res = requests.get(url)
    with open("load.jpg", "wb") as f:
        f.write(res.content)
    strdata = "import pyi_splash\npyi_splash.update_text('loaded...')\npyi_splash.close()"
    with open("loading_window.py", "w") as f:
        f.write(strdata)
    file_path = os.path.abspath(__file__)
    name_path = os.path.join(os.path.dirname(file_path), os.path.basename(file_path).split(".")[0] + ".exe")
    if os.path.exists(name_path):
        try:
            os.remove(name_path)
        except:
            wx.MessageBox("构建失败程序被占用", "提示",wx.OK, windows)
            windows.Show(True)
            windows.buttn_build.Enable(True)
            return
    pyscr = os.path.join(os.path.dirname(sys.executable), "Scripts")
    pips  = os.path.join(pyscr, "pip")
    pyinstallers = os.path.join(pyscr, "pyinstaller.exe")
    if not os.path.exists(pyinstallers):
        os.system("{} install pyinstaller".format(pips))
    f = os.popen(pyinstallers + " -v")
    ret = f.read()
    f.close()
    if ret.split("\n")[0] != "4.10" and ret.split("\n")[0][0] <= "4":
        os.system("{} install --upgrade  pyinstaller==4.10".format(pips))
    commd = "{} -Fw {} --splash load.jpg".format(pyinstallers, file_path)
    print("构建程序命令：", commd)
    print("开始构建...")
    os.system(commd)
    dist_path  = os.path.join(os.path.dirname(file_path), "dist")
    build_path = os.path.join(os.path.dirname(file_path), "build")
    spec_path  = os.path.join(os.path.dirname(file_path), os.path.basename(file_path).split(".")[0] + ".spec")
    if os.path.exists(build_path):
        shutil.rmtree(build_path)
    if os.path.exists(dist_path):
        name_path = os.path.join(dist_path, os.path.basename(file_path).split(".")[0] + ".exe")
        if os.path.exists(name_path):
            shutil.move(name_path, os.path.dirname(file_path))
        shutil.rmtree(dist_path)
    if os.path.exists(spec_path):
        os.remove(spec_path)
    os.remove("loading_window.py")
    os.remove("load.jpg")
    windows.Show(True)
    wx.MessageBox("构建完成", "提示",wx.OK, windows)
    windows.buttn_build.Enable(True)
    os.system("explorer.exe {}".format(os.path.dirname(file_path)))



class mylog(object):
    def __init__(self, log):
        self.log = log

    def write(self, message):
        with open(self.log, "a") as file:
            file.write(message)



if __name__ == '__main__':
    file_log = mylog('错误日志.log')
    sys.stderr = file_log
    #sys.exitfunc = file_log.close
    app = wx.App()
    maingui(None).Show(True)
    app.MainLoop()
